package com.chris.websocket.webdemo.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * ws协议的Controller
 */
@ServerEndpoint("/ws/{sid}")
@Component
public class WebSocketService {
    private static Logger logger = LoggerFactory.getLogger("websocket");

    //静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    //concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
    //在理想状态下，ConcurrentHashMap 可以支持 16 个线程执行并发写操作（如果并发级别设为16），及任意数量线程的读操作。
    private static ConcurrentHashMap<String,WebSocketService> webSocketMap = new ConcurrentHashMap<>();

    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session _session;

    //接收sid
    private String _sid ="";

    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        this._session = session;
        this._sid =sid;
        if(webSocketMap.containsKey(sid)){
            webSocketMap.remove(sid);
            webSocketMap.put(sid,this);
            //加入set中
        }else{
            //加入set中
            webSocketMap.put(sid,this);
            //在线数加1
            addOnlineCount();
        }

        logger.debug("有新连接加入："+session.getId()+",用户连接:"+sid+",当前在线人数为:" + getOnlineCount());

    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        if (webSocketMap.containsKey(_sid)) {
            webSocketMap.remove(_sid);
            subOnlineCount();           //在线数减1
            logger.info("有一连接 sid:"+ _sid +"->"+session.getId()+"关闭！当前在线人数为" + getOnlineCount());
        }
    }

    /**
     * 服务端收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        logger.info("收到来自客户端 "+session.getId()+" ->"+ _sid +" 的信息:"+message);
        sendMessage("收到信息："+message);
        /*

        */
    }


    /**
     * 服务端主动推送给该服务端对应对应客服端
     *对象内调用
     */
    private void sendMessage(String message) throws IOException {
        this._session.getBasicRemote().sendText(message);
    }

    /**
     * 群发消息(自己给所有其他客服端、排除自己）
     * 对象内调用
     * @param message
     *            消息内容
     */
    private void sendAllMessage(String message) {
        //群发消息
        for (Map.Entry  item : webSocketMap.entrySet()) {
            try {
                //Session toSession =  ((WebSocketService)item.getValue())._session;
                WebSocketService socketService =  ((WebSocketService)item.getValue());
                // 排除掉自己
                if (_sid.equals(socketService._sid))
                    continue;
                ((WebSocketService)item.getValue()).sendMessage(message);
            } catch (IOException e) {
                logger.error("用户ID:"+this._sid +" 群发失败,原因:"+e.getMessage());
                e.printStackTrace();
            }
        }
    }


    /**
     * 群发消息（宿主服务端给所有客服端）
     * 对象外调用
     * @param message
     *            消息内容
     */
    public static void  sendAllInfo(String message) {
        //群发消息
        for (Map.Entry  item : webSocketMap.entrySet()) {
            try {
                ((WebSocketService)item.getValue()).sendMessage(message);
            } catch (IOException e) {
                logger.error("群发失败,原因:"+e.getMessage());
                e.printStackTrace();
            }
        }
    }

    /**
     * 服务端发自定义消息给指定客服端
     * （本质就是找到该ID对应服务端，再推送给对应客服端）
     *  对象外调用
     * */
    public static void sendInfo(String message,@PathParam("sid") String sid) throws IOException {
        logger.info("推送消息到窗口" + sid + "，推送内容:" + message);
        if (webSocketMap.containsKey(sid))
        {
            webSocketMap.get(sid).sendMessage(message);
        }
        else{
            logger.debug("用户"+sid+",不在线！");
        }
    }



    /**
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        logger.error("用户错误:"+this._sid +",原因:"+error.getMessage());
        error.printStackTrace();
    }





    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketService.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketService.onlineCount--;
    }

}
